package jmetal.experiments.util;

import jmetal.experiments.Experiment;

import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;
import java.util.Vector;

public class Friedman {

    private Experiment exp_;

    public Friedman(Experiment exp) {
        exp_ = exp;
    }

    public void executeTest(String indic) {
        Vector algoritmos;
        Vector datasets;
        Vector datos;
        String cadena = "";
        StringTokenizer lineas;
        String linea;
        int i, j, k;
        int posicion;
        double mean[][];
        Pareja orden[][];
        Pareja rank[][];
        boolean encontrado;
        int ig;
        double sum;
        boolean visto[];
        Vector porVisitar;
        double Rj[];
        double friedman;
        double sumatoria = 0;
        double termino1, termino2;

        String indicator_ = indic;

        /*Read the result file*/
        String separator = System.getProperty("file.separator");

        String outDir = exp_.experimentBaseDirectory_ + separator + "latex";
        String outFile = outDir + separator + "FriedmanTest" + indicator_ + ".tex";

        String Output = "";
        Output = Output + ("\\documentclass{article}\n"
                + "\\usepackage{graphicx}\n"
                + "\\title{Results}\n"
                + "\\author{}\n"
                + "\\date{\\today}\n"
                + "\\begin{document}\n"
                + "\\oddsidemargin 0in \\topmargin 0in"
                + "\\maketitle\n"
                + "\\section{Friedman teste}");

        algoritmos = new Vector();
        datasets = new Vector();
        datos = new Vector();

        for (int alg = 0; alg < exp_.algorithmNameList_.length; alg++) {
            algoritmos.add(new String(exp_.algorithmNameList_[alg]));
            datos.add(new Vector());
            String rutaAlg = exp_.experimentBaseDirectory_ + separator + "data" + separator
                    + exp_.algorithmNameList_[alg] + separator;

            for (int prob = 0; prob < exp_.problemList_.length; prob++) {
                if (alg == 0) {
                    datasets.add(exp_.problemList_[prob]);
                }

                String ruta = rutaAlg + exp_.problemList_[prob] + separator + indicator_;

                //Leemos el fichero
                cadena = "";

                try {
                    FileInputStream fis = new FileInputStream(ruta);

                    byte[] leido = new byte[4096];
                    int bytesLeidos = 0;

                    while (bytesLeidos != -1) {
                        bytesLeidos = fis.read(leido);

                        if (bytesLeidos != -1) {
                            cadena += new String(leido, 0, bytesLeidos);
                        }
                    }

                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    System.exit(-1);
                }

                lineas = new StringTokenizer(cadena, "\n\r");

                double valor = 0.0;
                int n = 0;

                while (lineas.hasMoreTokens()) {
                    linea = lineas.nextToken();
                    valor = valor + Double.parseDouble(linea);
                    n++;
                }
                if (n != 0) {
                    ((Vector) datos.elementAt(alg)).add(new Double(valor / n));
                } else {
                    ((Vector) datos.elementAt(alg)).add(new Double(valor));
                }

            } // for
        } // for


        /*Compute the average performance per algorithm for each data set*/
        mean = new double[datasets.size()][algoritmos.size()];

        for (j = 0; j < algoritmos.size(); j++) {
            for (i = 0; i < datasets.size(); i++) {
                mean[i][j] = ((Double) ((Vector) datos.elementAt(j)).elementAt(i)).doubleValue();
            }
        }


        /*We use the pareja structure to compute and order rankings*/
        orden = new Pareja[datasets.size()][algoritmos.size()];
        for (i = 0; i < datasets.size(); i++) {
            for (j = 0; j < algoritmos.size(); j++) {
                orden[i][j] = new Pareja(j, mean[i][j]);
            }
            Arrays.sort(orden[i]);
        }

        /*building of the rankings table per algorithms and data sets*/
        rank = new Pareja[datasets.size()][algoritmos.size()];
        posicion = 0;
        for (i = 0; i < datasets.size(); i++) {
            for (j = 0; j < algoritmos.size(); j++) {
                encontrado = false;
                for (k = 0; k < algoritmos.size() && !encontrado; k++) {
                    if (orden[i][k].indice == j) {
                        encontrado = true;
                        posicion = k + 1;
                    }
                }
                rank[i][j] = new Pareja(posicion, orden[i][posicion - 1].valor);
            }
        }

        /*In the case of having the same performance, the rankings are equal*/
        for (i = 0; i < datasets.size(); i++) {
            visto = new boolean[algoritmos.size()];
            porVisitar = new Vector();

            Arrays.fill(visto, false);
            for (j = 0; j < algoritmos.size(); j++) {
                porVisitar.removeAllElements();
                sum = rank[i][j].indice;
                visto[j] = true;
                ig = 1;
                for (k = j + 1; k < algoritmos.size(); k++) {
                    if (rank[i][j].valor == rank[i][k].valor && !visto[k]) {
                        sum += rank[i][k].indice;
                        ig++;
                        porVisitar.add(new Integer(k));
                        visto[k] = true;
                    }
                }
                sum /= (double) ig;
                rank[i][j].indice = sum;
                for (k = 0; k < porVisitar.size(); k++) {
                    rank[i][((Integer) porVisitar.elementAt(k)).intValue()].indice = sum;
                }
            }
        }

        /*compute the average ranking for each algorithm*/
        Rj = new double[algoritmos.size()];
        for (i = 0; i < algoritmos.size(); i++) {
            Rj[i] = 0;
            for (j = 0; j < datasets.size(); j++) {
                Rj[i] += rank[j][i].indice / ((double) datasets.size());
            }
        }

        /*Print the average ranking per algorithm*/
        Output = Output + "\n" + ("\\begin{table}[!htp]\n"
                + "\\centering\n"
                + "\\caption{Media de ranking dos algoritmos\n}" +// for "+ exp_.problemList_[prob] +" problem\n}" +
                "\\begin{tabular}{c|c}\n"
                + "Algoritmo&Ranking\\\\\n\\hline");

        for (i = 0; i < algoritmos.size(); i++) {
            Output = Output + "\n" + (String) algoritmos.elementAt(i) + "&" + Rj[i] + "\\\\";
        }

        Output = Output + "\n"
                + "\\end{tabular}\n"
                + "\\end{table}";

        /*Compute the Friedman statistic*/
        termino1 = (12 * (double) datasets.size()) / ((double) algoritmos.size() * ((double) algoritmos.size() + 1));
        termino2 = (double) algoritmos.size() * ((double) algoritmos.size() + 1) * ((double) algoritmos.size() + 1) / (4.0);
        for (i = 0; i < algoritmos.size(); i++) {
            sumatoria += Rj[i] * Rj[i];
        }
        friedman = (sumatoria - termino2) * termino1;

        Output = Output + "\n" + "\n\nEstat\\'istica Friedman considerando redu\\c{c}\\~ao de performance (distribu\\'ido de acordo com o qui-quadrado com " + (algoritmos.size() - 1) + " graus de liberdade: " + friedman + ").\n\n";

        Output = Output + "\n" + "\\end{document}";
        try {
            File latexOutput;
            latexOutput = new File(outDir);
            if (!latexOutput.exists()) {
                latexOutput.mkdirs();
            }
            FileOutputStream f = new FileOutputStream(outFile);
            DataOutputStream fis = new DataOutputStream((OutputStream) f);

            fis.writeBytes(Output);

            fis.close();
            f.close();
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(-1);
        }
    }

}
